#pragma once

#include <iostream>
#include <memory>
#include <cstring>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <functional>
#include "Log.hpp"
#include "InetAddr.hpp"

//网络中的两个进程通信利用的是IP+Port，所以只要通信，就必须传参数IP+Port
//实验一下结构体的每个元素的地址是否位逐个增大
using namespace LogModule;
using func_t=std::function<void(int sockfd,const std::string&,InetAddr&)>;

class UdpServe
{
public:
    // UdpServe(const std::string ip,uint16_t port)
    UdpServe(uint16_t port,func_t func)
        :_socketfd(-1),
        //  _ip(ip),
         _port(port),
         _func_t(func),
         _isrunning(false)
         
    {}
    void Init()
    {
        //1.创建套接字
        _socketfd=socket(AF_INET,SOCK_DGRAM,0);
        if(_socketfd<0)
        {
            LOG(FATAL)<<"scoket create error "<<"socketfd: "<<_socketfd;
            exit(1);
        }
        LOG(INFO)<<"scoket create success "<<"socketfd: "<<_socketfd;
        //2绑定socket信息，端口号和IP信息
        struct sockaddr_in local;
        
        //2.1填充sockaddr_in结构体
        bzero(&local,sizeof(local));
        //客户端需要发送什么？我的IP和端口号，一定要发送
        //2.2转换ip地址序列为网络序列
        //类内属性IP是一个string类型，需要先转化成4字节，再转换为网络序列

        local.sin_family=AF_INET;
        local.sin_port=htons(_port);
        local.sin_addr.s_addr=INADDR_ANY;

        // local.sin_addr.s_addr=inet_addr(_ip.c_str());
        // local.sin_addr.s_addr=INADDR_ANY;
        //我现在只是在用户的栈上绑定了，内核并未绑定
        //3.和内核绑定端口号bind
        //这一步只是再用户栈上绑定了，还需要给内核
        //将用户指定的地址与内核中的struct socket绑定
        int m=bind(_socketfd,(struct sockaddr*)&local,sizeof local);
        //服务端需要显式的绑定端口号，IP和端口号是不能轻易改变的，不能随机分配
        if(m<0)
        {
            LOG(FATAL)<<"bind errror";
            exit(2);
        }
        LOG(INFO)<<"bind success,_socketfd:"<<_socketfd;
    }
    void Start()
    {
        //死循环运行
        _isrunning=true;
        while(_isrunning)
        {
            //当前写的是udp，是无连接的，实验无连接的接口
            //收消息
            char buffer[1024];
            struct sockaddr_in peer;
            socklen_t len=sizeof peer;
            ssize_t m=recvfrom(_socketfd,buffer,sizeof(buffer)-1,0,(struct sockaddr*)&peer,&len);
            
            if(m<0)
            {
                LOG(FATAL)<<"receive error";
                continue;
            }

            //发消息
            //接收的是网络序列，处理消息需要先转换格式
            LOG(INFO)<<"receive success";
            //将网络序列转换成host byte order（主机字节序）(大端或者小端)
            InetAddr client(peer);

            buffer[m]=0;

            //谁发的消息和内容
            // LOG(DEBUG)<<"["<<client.IP()<<":"<<client.PORT()<<"] #"<<buffer;
            // std::string res="serve@";
            // res+=buffer;
            _func_t(_socketfd,buffer,client);
            // std::cout<<std::endl;
            // res.insert(0,"serve@");
            // sendto(_socketfd,res.c_str(),res.size(),0,(struct sockaddr*)&peer,len);
        }
    }
    ~UdpServe(){}
private:
    int _socketfd; 
    // std::string _ip;
    func_t _func_t;
    bool _isrunning;
    uint16_t _port;
};